package com.plian.system.mybatis.generator;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.TemplateType;
import com.baomidou.mybatisplus.generator.config.builder.CustomFile;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;
import com.plian.Tools.Func;
import com.plian.Tools.StringUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;

import java.io.File;
import java.io.IOException;
import java.sql.Types;
import java.util.*;

/**
 * 代码生成器配置类
 *
 */
@Data
@Slf4j
public class GzwCodeGenerator {

	private static final String BASE_CONTROLLER_PATH = "com.plian.Tools.base.BaseController";
	private static final String BASE_SERVICE_PATH = "com.plian.system.mybatis.base.BaseService";
	private static final String BASE_SERVICE_IMPL_PATH = "com.plian.system.mybatis.base.BaseServiceImpl";
	private static final String BASE_ENTITY_PATH = "com.plian.system.mybatis.base.BaseEntity";

	/**
	 * 代码模块名称
	 */
	private String codeName;

	/**
	 * 代码生成的包名
	 */
	private String packageName = "com.plian.system";

	private String xmlPackageName = "com.plian.system";

	/**
	 * 代码所在服务名
	 */
	private String serviceName = "sys";

	/**
	 * 业务
	 */
	private String moduleName = "test";

	/**
	 * 代码后端生成的地址
	 */
	private String packageDir;
	/**
	 * 需要去掉的表前缀
	 */
	private String[] tablePrefix = {"sys_"};
	/**
	 * 需要生成的表名(两者只能取其一)
	 */
	private String[] includeTables = {"sys_test"};
	/**
	 * 需要排除的表名(两者只能取其一)
	 */
	private String[] excludeTables = {};
	/**
	 * 是否包含基础业务字段
	 */
	private Boolean hasSuperEntity = Boolean.TRUE;
	/**
	 * 是否包含包装器
	 */
	private Boolean hasWrapper = Boolean.TRUE;

	/**
	 * 是否包含流程
	 */
	private Boolean hasFlow = Boolean.TRUE;

	/**
	 * 是否包含服务名
	 */
	private Boolean hasServiceName = Boolean.FALSE;
	/**
	 * 基础业务字段
	 */
	private String[] superEntityColumns = {"id", "create_time", "create_user", "create_dept", "update_time", "update_user", "status", "is_deleted", "create_company_id"};
	/**
	 * 租户字段
	 */
	private String tenantColumn = "tenant_id";
	/**
	 * 数据库驱动名
	 */
	private String driverName;
	/**
	 * 数据库链接地址
	 */
	private String url;
	/**
	 * 数据库用户名
	 */
	private String username;
	/**
	 * 数据库密码
	 */
	private String password;

	private String formType;

	/**
	 * 主键类型
	 */
	private IdType idType = IdType.ASSIGN_ID;

	/**
	 * 代码生成执行
	 */
	public void run() {
		Properties props = getProperties();
		String url = Func.toStr(this.url, props.getProperty("spring.datasource.url"));
		String username = Func.toStr(this.username, props.getProperty("spring.datasource.username"));
		String password = Func.toStr(this.password, props.getProperty("spring.datasource.password"));
		String servicePackage = serviceName.split("-").length > 1 ? serviceName.split("-")[1] : serviceName;

		Map<String, Object> customMap = new HashMap<>(11);
		customMap.put("codeName", codeName);
		customMap.put("serviceName", serviceName);
		customMap.put("servicePackage", servicePackage);
		customMap.put("servicePackageLowerCase", servicePackage.toLowerCase());
		customMap.put("tenantColumn", tenantColumn);
		customMap.put("hasWrapper", hasWrapper);
		customMap.put("hasServiceName", hasServiceName);
		customMap.put("hasFlow", hasFlow);
		customMap.put("formType", formType);
		customMap.put("hasSuperEntity", hasSuperEntity);
		Map<String, String> customFile = new HashMap<>(15);
		customFile.put("entityVO.java", "/templates/entityVO.java.vm");
		customFile.put("entityDTO.java", "/templates/entityDTO.java.vm");
		if (hasWrapper) {
			customMap.put("wrapperPath", packageName + "wrapper" + StringPool.DOT + serviceName + StringPool.DOT + moduleName);
			customFile.put("wrapper.java", "/templates/wrapper.java.vm");
		}
		if (!hasSuperEntity){
			superEntityColumns = new String[]{};
		}
		FastAutoGenerator.create(url, username, password)
			.dataSourceConfig(builder -> builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
				int typeCode = metaInfo.getJdbcType().TYPE_CODE;
				if (typeCode == Types.SMALLINT) {
					// 自定义类型转换
					return DbColumnType.INTEGER;
				}
				if (typeCode == Types.DATE) {
					// 自定义类型转换
					return DbColumnType.DATE;
				}
				if (typeCode == Types.TIME) {
					// 自定义类型转换
					return DbColumnType.DATE;
				}
				if (typeCode == Types.TIMESTAMP) {
					// 自定义类型转换
					return DbColumnType.DATE;
				}
				return typeRegistry.getColumnType(metaInfo);

			}))
			.globalConfig(builder -> builder.author(props.getProperty("author")).dateType(DateType.TIME_PACK).enableSwagger().outputDir(getOutputDir()).disableOpenDir())
			.packageConfig(builder -> builder.parent(packageName).controller("controller" + StringPool.DOT + serviceName + StringPool.DOT + moduleName)
					.entity("entity" + StringPool.DOT + serviceName + StringPool.DOT + moduleName)
					.service("service" + StringPool.DOT + serviceName + StringPool.DOT + moduleName)
					.serviceImpl("service" + StringPool.DOT + serviceName + StringPool.DOT + moduleName + StringPool.DOT + "impl")
					.mapper("mapper" + StringPool.DOT + serviceName + StringPool.DOT + moduleName)
					.pathInfo(Collections.singletonMap(OutputFile.xml, packageDir + "/src/main/resources/mybatis/mapper/" + serviceName)))
			.strategyConfig(builder -> builder.addTablePrefix(tablePrefix).addInclude(includeTables).addExclude(excludeTables)
				.entityBuilder().naming(NamingStrategy.underline_to_camel).columnNaming(NamingStrategy.underline_to_camel).enableLombok()
					.superClass(hasSuperEntity ? BASE_ENTITY_PATH : null).addSuperEntityColumns(superEntityColumns).enableFileOverride()
					.idType(hasSuperEntity ? null : idType)
				.serviceBuilder().superServiceClass(BASE_SERVICE_PATH).superServiceImplClass(BASE_SERVICE_IMPL_PATH)
					.formatServiceFileName("I%sService").formatServiceImplFileName("%sServiceImpl").enableFileOverride()
				.mapperBuilder().mapperAnnotation(Mapper.class).enableBaseResultMap().enableBaseColumnList().formatMapperFileName("%sMapper").formatXmlFileName("%sMapper").enableFileOverride()
				.controllerBuilder().superClass(BASE_CONTROLLER_PATH).formatFileName("%sController").enableRestStyle().enableHyphenStyle().enableFileOverride()
			)
			.templateConfig(builder -> builder.disable(TemplateType.ENTITY)
				.entity("/templates/entity.java.vm")
				.service("/templates/service.java.vm")
				.serviceImpl("/templates/serviceImpl.java.vm")
				.mapper("/templates/mapper.java.vm")
				.xml("/templates/mapper.xml.vm")
				.controller("/templates/controller.java.vm"))
			.injectionConfig(builder -> builder.beforeOutputFile(
					(tableInfo, objectMap) -> System.out.println("tableInfo: " + tableInfo.getEntityName() + " objectMap: " + objectMap.size())
				).customMap(customMap).customFile(customFile)
			)
			.templateEngine(new VelocityTemplateEngine() {
				@Override
				protected void outputCustomFile(List<CustomFile> customFiles, TableInfo tableInfo, Map<String, Object> objectMap) {
					String entityName = tableInfo.getEntityName();
					String entityNameLower = tableInfo.getEntityName().toLowerCase();

					customFiles.forEach(customFile -> {
						String key = customFile.getFileName();
						String value = customFile.getTemplatePath();
						String outputPath = getPathInfo(OutputFile.parent);
						objectMap.put("entityKey", entityNameLower);
						if (StringUtil.equals(key, "entityVO.java")) {
							outputPath = getOutputDir() + StringPool.SLASH + packageName.replace(StringPool.DOT, StringPool.SLASH)
									+ StringPool.SLASH + "vo" + StringPool.SLASH + serviceName  + StringPool.SLASH + moduleName + StringPool.SLASH + entityName + "VO" + StringPool.DOT_JAVA;
						}

						if (StringUtil.equals(key, "entityDTO.java")) {
							outputPath = getOutputDir() + StringPool.SLASH + packageName.replace(StringPool.DOT, StringPool.SLASH)
									+ StringPool.SLASH + "dto" + StringPool.SLASH + serviceName  + StringPool.SLASH + moduleName + StringPool.SLASH + entityName + "DTO" + StringPool.DOT_JAVA;
						}

						if (StringUtil.equals(key, "wrapper.java")) {
							outputPath = getOutputDir() + StringPool.SLASH + packageName.replace(StringPool.DOT, StringPool.SLASH)
									+ StringPool.SLASH + "wrapper" + StringPool.SLASH + serviceName  + StringPool.SLASH + moduleName + StringPool.SLASH + entityName + "Wrapper" + StringPool.DOT_JAVA;
						}

						outputFile(new File(String.valueOf(outputPath)), objectMap, value, Boolean.TRUE);
					});
				}
			}).execute();

	}


	/**
	 * 获取配置文件
	 *
	 * @return 配置Props
	 */
	private Properties getProperties() {
		// 读取配置文件
		Resource resource = new ClassPathResource("/templates/code.properties");
		Properties props = new Properties();
		try {
			props = PropertiesLoaderUtils.loadProperties(resource);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return props;
	}

	/**
	 * 生成到项目中
	 *
	 * @return outputDir
	 */
	public String getOutputDir() {
		return (Func.isBlank(packageDir) ? System.getProperty("user.dir") : packageDir) + "/src/main/java";
	}

}
